package com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.repository;

import com.iwhalecloud.citybrain.flow.platform.api.FlowClientService;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.BaseProcessService;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.FlowBaseDataHandleProcessor;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.UserService;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.impl.task.UserInfoHandleProcessor;
import com.iwhalecloud.citybrain.flow.platform.manager.application.utils.FlowUtils;
import com.iwhalecloud.citybrain.flow.platform.manager.application.utils.SpringUtils;
import com.iwhalecloud.citybrain.flow.platform.manager.common.ErrorCode;
import com.iwhalecloud.citybrain.flow.platform.manager.common.ResponseResult;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.enums.MultiInstanceKeyEnum;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.*;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.factory.NodeCompletionConditionFactory;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.task.SupplyPersonVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.task.SupplySignTaskVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.*;
import com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.entities.FlowReNodeAuth;
import com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.entities.FlowTaskTrace;
import com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.entities.FlowTaskTraceExample;
import com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.mapper.FlowReNodeAuthDAO;
import com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.mapper.FlowTaskTraceDAO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.flowable.engine.HistoryService;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.service.impl.persistence.entity.TaskEntityImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author lzb
 * @date 2021-11-29
 */
@Slf4j
@Service
public class FlowSupplyNodeServiceImpl extends BaseProcessService implements FlowSupplyNodeService {

    //是否有补签权限
    private final String ISREISSUE = "reissue";

    @Autowired
    private UserInfoHandleProcessor userInfoHandleProcessor;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    FlowFormRepository flowFormRepository;

    @Autowired
    private FlowTaskTraceDAO flowTaskTraceDAO;

    @Autowired
    FlowReNodeAuthDAO flowReNodeAuthDAO;

    @Autowired
    private UserService userService;

    @Autowired
    private NodeCompletionConditionFactory nodeCompletionConditionFactory;

    @Autowired
    private HistoryService historyService;

    @Autowired
    FlowClientService flowClientService;

    @Autowired
    private FlowBaseDataHandleProcessor flowBaseDataHandleProcessor;

    @Autowired
    private FlowTaskTraceRepository flowTaskTraceRepository;


    @Autowired
    private FlowProcessInstanceTraceRepository flowProcessInstanceTraceRepository;

    @Autowired
    FlowMonitorRepository flowMonitorRepository;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResult supplySignTask(SupplySignTaskVo supplySignTaskVo) {
        //流程实例id
        String processInstanceId = supplySignTaskVo.getProcessInstanceId();
        //根据流程实例获取并检查流程实例是否存在
        boolean isExistProcessInstance = flowBaseDataHandleProcessor.isExistCheckProcessInstance(processInstanceId);
        if (!isExistProcessInstance) {
            return ResponseResult.fail(ErrorCode.PROCESS_INSTANCE_NOT_EXIST.errCode(), ErrorCode.PROCESS_INSTANCE_NOT_EXIST.errMessage());
        }
        //补发人员列表
        List<SupplyPersonVo> supplyPersonList = supplySignTaskVo.getSupplyPersonList();
        if (CollectionUtils.isEmpty(supplyPersonList)) {
            return ResponseResult.fail(ErrorCode.REISSUE_USER_NOT_EXIST.errCode(), ErrorCode.REISSUE_USER_NOT_EXIST.errMessage());
        }
        //当前节点任务id
        String taskId = supplySignTaskVo.getTaskId();
        HistoricTaskInstance historyTask = historyService.createHistoricTaskInstanceQuery().taskId(taskId).singleResult();
        ProcessInstance historyTaskProcessInstance = runtimeService.createProcessInstanceQuery().processInstanceId(historyTask.getProcessInstanceId()).singleResult();
        //校验操作者权限
        if (!Objects.equals(historyTask.getAssignee(),supplySignTaskVo.getUserCode())) {
            return ResponseResult.fail(ErrorCode.IDENTITY_MISSING.errCode(), "处理人不匹配，无操作权限");
        }

        String historyTaskDeploymentId = historyTaskProcessInstance.getDeploymentId();
        String historyNodeCode = historyTask.getTaskDefinitionKey();
        FlowReNodeAuth flowReNodeAuth = flowReNodeAuthDAO.selectByDeployIdAndNodeCodeAndAuthName(historyTaskDeploymentId, historyNodeCode, ISREISSUE);
        //是否有补发权限
        if (flowReNodeAuth == null || flowReNodeAuth.getAuthSwitch() != true) {
            return ResponseResult.fail(ErrorCode.NO_REISSUE_SIGN_AUTH.errCode(), ErrorCode.NO_REISSUE_SIGN_AUTH.errMessage());
        }
        List<Task> task = taskService.createTaskQuery().processInstanceId(processInstanceId).list();
        Set<String> nodeCodeSet = task.stream().map(Task::getTaskDefinitionKey).collect(Collectors.toSet());

        if (nodeCodeSet.size() != 1) {
            return ResponseResult.fail(ErrorCode.MULTI_NEXT_NODE.errCode(), ErrorCode.MULTI_NEXT_NODE.errMessage());
        }

        //如果正在运行的任务包含历史任务，正在运行的任务为二次运行（暂时不考虑子流程）
        //不能自己给自己补发
        if (nodeCodeSet.contains(historyTask.getTaskDefinitionKey())){
            return ResponseResult.fail(ErrorCode.NO_REISSUE_SELF.errCode(), "当前任务节点["+historyTask.getName()+"]与正在运行的任务节点为同一个节点，请通过加签方式实现");
        }

        //节点编码
        String nextTaskId = task.get(0).getId();
        String nodeCode = task.get(0).getTaskDefinitionKey();
//        String processDefinitionId = task.get(0).getProcessDefinitionId();
//        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
        //是否会签节点
        boolean isMultiTask = FlowUtils.getInstance().isMultiTaskActivity(processInstanceId, nodeCode);
        if (!isMultiTask) {
            return ResponseResult.fail(ErrorCode.NOT_MULTI_TASK.errCode(), ErrorCode.NOT_MULTI_TASK.errMessage());
        }

        FlowTaskTraceExample flowTaskTraceExample = new FlowTaskTraceExample();
        flowTaskTraceExample.createCriteria().andTaskIdEqualTo(nextTaskId).andProcessInstanceIdEqualTo(processInstanceId);
        List<FlowTaskTrace> flowTaskTraces = flowTaskTraceDAO.selectByExample(flowTaskTraceExample);

        flowTaskTraceExample = new FlowTaskTraceExample();
        flowTaskTraceExample.createCriteria().andParentIdEqualTo(flowTaskTraces.get(0).getParentId()).andProcessInstanceIdEqualTo(processInstanceId);
        //获取所有的兄弟任务
        List<FlowTaskTrace> broFlowTasks = flowTaskTraceDAO.selectByExample(flowTaskTraceExample);
        Map<String,String> multiTaskOperators = new HashMap<>();

        if (CollectionUtils.isNotEmpty(broFlowTasks)){
            broFlowTasks.stream().forEach(flowTaskTrace -> {
                multiTaskOperators.put(flowTaskTrace.getOperatorCode()
                        , StringUtils.isNotBlank(flowTaskTrace.getOperator()) ? flowTaskTrace.getOperator() : flowTaskTrace.getOperatorCode());
            });
        }

        //补发节点人员
        for (SupplyPersonVo supplyPerson : supplyPersonList) {

            if (MapUtils.isNotEmpty(multiTaskOperators) && multiTaskOperators.containsKey(supplyPerson.getCode())){
                return ResponseResult.fail(ErrorCode.ADD_SIGN_TASK_FAIL.errCode(),"用户["+multiTaskOperators.get(supplyPerson.getCode())+"]已在当前会签列表中");
            }

            // 检查是否需要设置用户变量信息
            runtimeService.addMultiInstanceExecution(nodeCode,
                    processInstanceId, Collections.singletonMap("assignee", supplyPerson.getCode()));
        }

        //加签之后重新计算需处理人数
        recalculateHandler(task.get(0), flowTaskTraces.get(0).getParentId());
        return ResponseResult.success("补发成功");
    }


    public void recalculateHandler(Task task, String parentExecutionId) {
        String processDefinitionId = task.getProcessDefinitionId();
        ProcessDefinition processDefinition = repositoryService.getProcessDefinition(processDefinitionId);

        String nodeCode = task.getTaskDefinitionKey();

        String completionCondition = null;
        List<CompletionCondition> completionConditionList = nodeCompletionConditionFactory.getCompletionConditons(processDefinition.getDeploymentId(), nodeCode);
        if (completionConditionList != null && completionConditionList.size() > 0) {
            completionCondition = completionConditionList.get(0).getValue();
        }

        int numOfHandler = 0;

        if (Objects.nonNull(completionCondition)) {

            String index;
            if (completionCondition.contains(">=")) {
                //解析出后面的数字
                index = "=";
            } else {
                index = ">";
            }
            String digital = completionCondition.substring(completionCondition.indexOf(index) + 1, completionCondition.length() - 1);
            int numOfInstances = (int) taskService.getVariable(task.getId(), MultiInstanceKeyEnum.NUMBER_OF_INSTANCES.getCode());
            if (completionCondition.contains("/")) {
                double rightValue = Double.parseDouble(digital) * numOfInstances;
                if (completionCondition.contains(">=")) {
                    numOfHandler = (int) Math.ceil(rightValue);
                } else {
                    numOfHandler = (int) Math.floor(rightValue + 1);
                }
            } else {
                if (completionCondition.contains(">=")) {
                    numOfHandler = Math.min(Integer.parseInt(digital), numOfInstances);
                } else {
                    numOfHandler = Math.min(Integer.parseInt(digital) + 1, numOfInstances);
                }
            }

            FlowMultiInstanceTaskTraceRepository multiInstanceTaskTraceService = SpringUtils.getBean(FlowMultiInstanceTaskTraceRepository.class);
            FlowMultiInstanceTaskTraceVo multiInstanceTaskTrace = multiInstanceTaskTraceService.getByRootExecutionId(parentExecutionId);
            if (Objects.nonNull(multiInstanceTaskTrace)) {
                multiInstanceTaskTrace.setNumOfHandler(numOfHandler);
                multiInstanceTaskTrace.setNumOfInstances(numOfInstances);
                multiInstanceTaskTraceService.updateMultiInstanceTaskTrace(multiInstanceTaskTrace);
            }

        }
    }

}